package cn.antia.db;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * 事务管理器
 * User: Jair Shaw
 * Date: 2008-9-10
 * Time: 17:57:30
 */
public class TransactionUtils {
    private static final ThreadLocal<TrascationConnection> localConnection = new ThreadLocal<TrascationConnection>();

    /**
     * 开始一个事务
     */
    public static void begin() {
        try {
            TrascationConnection tc = localConnection.get();
            if (tc == null) {
                Connection conn = DbUtils.getConnection();
                conn.setAutoCommit(false);
                localConnection.set(new TrascationConnection(conn));
            }else {
                //事务嵌套层数加一
                tc.setNestedLayers(tc.getNestedLayers() + 1);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 提交事务
     */
    public static void commit() {
        try {
            TrascationConnection tc = localConnection.get();
            if (tc == null) {
                throw new TransactionException("提交事务失败：没有取得属于事务的数据库连接");
            } else {
                //如果嵌套层次大于0，则不提交事务
                int nl = tc.getNestedLayers();
                if (nl > 0) {
                    tc.setNestedLayers(nl - 1);
                }else{
                    Connection conn = tc.getConn();
                    conn.commit();
                    conn.close();
                    localConnection.remove();
                }
            }
        } catch (SQLException e) {      
            throw new TransactionException("提交事务失败：提交事务时出现异常",e);
        }
    }

    /**
     * 回滚事务
     * @param e Exception     */
    public static void rollback(Throwable e) {
        try {
            TrascationConnection tc = localConnection.get();
            if (tc == null) {
                throw new TransactionException("回滚事务失败：没有取得属于事务的数据库连接");
            } else {
                //如果嵌套层次大于0，则设置事务已经死掉了
                int nl = tc.getNestedLayers();
                if (nl > 0) {
                    tc.setNestedLayers(nl - 1);
                    throw new TransactionException("该事务由于之前的子事务已经回滚，所以事务也将回滚",e);
                }else{
                    Connection conn = tc.getConn();
                    conn.rollback();
                    conn.close();
                    localConnection.remove();
                    throw new TransactionException("事务已经正常回滚",e);
                }
            }
        } catch (SQLException ee) {
            throw new TransactionException("回滚事务失败：出现SQL异常。",ee);
        }
    }
    /**
     * 回滚事务*/
    public static void rollback() {
        rollback(new RuntimeException("用户回滚事务"));
    }
    /**
     * 回滚事务
     * 不抛出异常
     * */
    public static void rollbackQuietly() {
        try {
            rollback(new RuntimeException("用户回滚事务"));
        } catch (Exception e){
            //quite
        }
    }

    /**
     * 当前线程是否在事务中
     * @return bool
     */
    public static boolean isInTransaction() {
        return localConnection.get() != null;
    }

    /**
     * 此数据库连接是否在事务中
     * @param conn Connection
     * @return bool
     */
    public static boolean isConnInTransaction(Connection conn) {
        TrascationConnection tc = localConnection.get();
        return tc != null && tc.getConn() == conn;
    }

    /**
     * 取得当前线程的数据库连接
     * @return Connection
     */
    public static Connection currentConnection() {
        TrascationConnection tc = localConnection.get();
        if (tc == null) {
            throw new TransactionException("取当前数据库连接失败：没有取得属于事务的数据库连接");
        } else {
            return tc.getConn();
        }
    }

    static class TrascationConnection{
        Connection conn;        //DB connection
        int nestedLayers;       //嵌套层数

        public Connection getConn() {
            return conn;
        }
        public void setConn(Connection conn) {
            this.conn = conn;
        }
        public int getNestedLayers() {
            return nestedLayers;
        }
        public void setNestedLayers(int nestedLayers) {
            this.nestedLayers = nestedLayers;
        }

        TrascationConnection(Connection conn) {
            this.conn = conn;
        }
    }
}
